/*******************************************************************************
 * Copyright (c) 2012 laurent barbisan.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    laurent barbisan - initial API and implementation
 *******************************************************************************/ 

package fr.free.hd.servers.android.geonaute.connector;

import java.util.Iterator;
import java.util.List;

import net.smartam.leeloo.client.OAuthClient;
import net.smartam.leeloo.client.URLConnectionClient;
import net.smartam.leeloo.client.request.OAuthClientRequest;
import net.smartam.leeloo.client.response.OAuthJSONAccessTokenResponse;
import net.smartam.leeloo.common.exception.OAuthProblemException;
import net.smartam.leeloo.common.exception.OAuthSystemException;
import net.smartam.leeloo.common.message.types.GrantType;
import android.content.SharedPreferences;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.net.Uri;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;

import com.isobar.isohealth.GraphConstants;
import com.isobar.isohealth.models.NewFitnessActivity;
import com.isobar.isohealth.wrappers.FitnessActivityWrapper;
import com.isobar.isohealth.wrappers.RunkeeperService;

import fr.free.hd.servers.android.geonaute.connector.devices.DeviceGH615;
import fr.free.hd.servers.android.geonaute.connector.model.Track;
import fr.free.hd.servers.android.geonaute.connector.model.TrackLap;
import fr.free.hd.servers.android.geonaute.connector.model.TrackPoint;

public class LoadTrackTask extends AsyncTask<LoadTrackInfoParams, Track, Void> {
	
		/**
		 * Log ID
		 */
		private static final String LOG_TAG = "LoadTrackTask";	
	
		private LoadTrackInfoParams loadTrackInfoParams;

		protected void onProgressUpdate(Track... progress) {
			
	     }

	     protected void onPostExecute(Long result) {
	       
	     }

		@Override
		protected Void doInBackground(LoadTrackInfoParams... params) {

			loadTrackInfoParams = params[0];
			
			try
			{
			Uri uri = loadTrackInfoParams.getIndent().getData();
			/*if (uri != null
					&& uri.toString().startsWith("oauth://geonaute.connector")) {*/
				String code = uri.getQueryParameter("code");

				OAuthClientRequest request;

				request = OAuthClientRequest
						.tokenLocation(GraphConstants.ACCESS_URL)
						.setGrantType(GrantType.AUTHORIZATION_CODE)
						.setClientId(GraphConstants.CLIENT_ID)
						.setClientSecret(GraphConstants.CLIENT_SECRET)
						.setRedirectURI(GraphConstants.REDIRECT_URI)
						.setCode(code).buildBodyMessage();
				OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
				OAuthJSONAccessTokenResponse response = oAuthClient.accessToken(request);

				String accessToken = response.getAccessToken();
				String expiresIn = response.getExpiresIn();

				SharedPreferences pref = loadTrackInfoParams.getPreferences();
				pref.edit().putString(MainMenuActivity.PREFS_TOKEN, accessToken).commit();
				
				Log.i(LOG_TAG, "Access Token: " + accessToken + " will expire in " + expiresIn);
			
			} 
			catch (OAuthProblemException e) {
				Log.e(LOG_TAG, "Unable to get credentials from Run Keeper : " + e.getMessage());
			} catch (OAuthSystemException e) {
				Log.e(LOG_TAG, "Unable to get credentials from Run Keeper : " + e.getMessage());
			}
			
			UsbDeviceConnection connection = loadTrackInfoParams.getUsbConnection();
			UsbEndpoint epIN = loadTrackInfoParams.getEpIN();
			UsbEndpoint epOUT = loadTrackInfoParams.getEpOUT();
		
			/*fillTracks(loadTrackInfoParams.getTrackIds(), connection, epIN, epOUT);*/
			
			for (int i = 0; i < loadTrackInfoParams.getTrackIds().size(); i++) {
				Track result = fillTrack(loadTrackInfoParams.getTrackIds().get(i), connection, epIN, epOUT);
				//Convert Track
				NewFitnessActivity activity = TrackConverter.convertTrackToRunKeeper(result);
				
				//Send Track
				SharedPreferences pref = loadTrackInfoParams.getSharedPreferences();
				String auth = pref.getString(MainMenuActivity.PREFS_TOKEN, null);
				RunkeeperService runkeeperService = new RunkeeperService(auth);
				FitnessActivityWrapper fitnessActivityWrapper = runkeeperService.fitnessActivityWrapper;
				try {
					fitnessActivityWrapper.createFitnessActivity(activity);
				} catch (Exception ex) {
					Log.e("LoadTrackTask", ex.getMessage());
				}
				
				onProgressUpdate(result);
			}

			return null;
		}
		
		 private void fillTracks(List<Track> trackIds, UsbDeviceConnection connection, UsbEndpoint epIN, UsbEndpoint epOUT) {
			 	
			 	byte[] array = new byte[3 + trackIds.size() * 2];
			 	short size = (short)trackIds.size();
				array[0] = (byte) 128;
				array[1] = (byte)(size & 0xff);
				array[2] = (byte)((size >> 8) & 0xff);				
				int num = 3;
				for (Track track : trackIds) {
					short id = (short)track.ID.intValue();
					array[num] = (byte)(id & 0xff);
					array[num+1] = (byte)((id >> 8) & 0xff);
					num += 2;
				}
				
				ConstructPayload(array);
			
				 //Todo check
		        connection.bulkTransfer(epOUT, array, array.length, 500);
		        
		        byte[] tseg = new byte[4096]; // 2070
		        connection.bulkTransfer(epOUT, tseg, tseg.length, 5000);
				
		}
		 
		 public static byte[] ConstructPayload(byte[] payload)
		 {
		 	short num = (short)payload.length;
		 	byte b =  (byte)((num >> 8) & 0xff);
		 	byte b2 = (byte)(num & 0xff);
		 	byte[] array = new byte[(int)(4 + num)];
		 	array[0] = 2;
		 	array[1] = b;
		 	array[2] = b2;
		 	byte b3 = 0;
		 	b3 ^= b;
		 	b3 ^= b2;
		 	for (int i = 0; i < (int)num; i++)
		 	{
		 		array[3 + i] = payload[i];
		 		b3 ^= payload[i];
		 	}
		 	array[(int)(num + 3)] = b3;
		 	return array;
		 }

		public static Track fillTrack(Track track, UsbDeviceConnection connection, UsbEndpoint in, UsbEndpoint out)
		    {        
		    	Integer TrackID = track.ID;
		    	
		        byte[] command_b = new byte[5];
		        byte[] command_b_t = new byte[9];

		        command_b_t[0] = (byte) 0x02;
		        command_b_t[1] = (byte) 0x00;
		        command_b_t[2] = (byte) 0x05;
		        command_b_t[3] = (byte) 0x80;
		        command_b_t[4] = (byte) 0x00;
		        command_b_t[5] = (byte) 0x01;
		        command_b_t[6] = (byte) (TrackID / 256);
		        command_b_t[7] = (byte) (TrackID - command_b_t[6]);

		        byte checksum = (byte) 0x00;
		        checksum ^= command_b_t[2];
		        checksum ^= command_b_t[3];
		        checksum ^= command_b_t[4];
		        checksum ^= command_b_t[5];
		        checksum ^= command_b_t[6];
		        checksum ^= command_b_t[7];
		        command_b_t[8] = checksum;

		        //Todo check
		        connection.bulkTransfer(out, command_b_t, command_b_t.length, 500);
		        
		        byte[] tseg = new byte[4096]; // 2070
		        connection.bulkTransfer(in, tseg, tseg.length, 5000);

		        int SegCounter = 0;
		        while (SegCounter < 500) {
		            int sp = 30;

		            Integer trg = DeviceGH615.uByteToInt(tseg[sp]) * 256 + DeviceGH615.uByteToInt(tseg[sp + 1]);

		            if (trg.equals(0xffff)) {
		                sp = 34;
		                while (sp < 2057) {
		                    TrackLap tl = new TrackLap();

		                    tl.Stop =
		                            DeviceGH615.uByteToInt(tseg[sp + 0]) * 16777216
		                            + DeviceGH615.uByteToInt(tseg[sp + 1]) * 65536
		                            + DeviceGH615.uByteToInt(tseg[sp + 2]) * 256
		                            + DeviceGH615.uByteToInt(tseg[sp + 3]);
		                    sp++;
		                    sp++;
		                    sp++;
		                    sp++;

		                    tl.Duration =
		                            DeviceGH615.uByteToInt(tseg[sp + 0]) * 16777216
		                            + DeviceGH615.uByteToInt(tseg[sp + 1]) * 65536
		                            + DeviceGH615.uByteToInt(tseg[sp + 2]) * 256
		                            + DeviceGH615.uByteToInt(tseg[sp + 3]);
		                    sp++;
		                    sp++;
		                    sp++;
		                    sp++;

		                    tl.Distance =
		                            DeviceGH615.uByteToInt(tseg[sp + 0]) * 16777216
		                            + DeviceGH615.uByteToInt(tseg[sp + 1]) * 65536
		                            + DeviceGH615.uByteToInt(tseg[sp + 2]) * 256
		                            + DeviceGH615.uByteToInt(tseg[sp + 3]);
		                    sp++;
		                    sp++;
		                    sp++;
		                    sp++;

		                    tl.Calories = DeviceGH615.uByteToInt(tseg[sp]) * 256 + DeviceGH615.uByteToInt(tseg[sp + 1]);
		                    sp++;
		                    sp++;

		                    tl.MaxSpeed = DeviceGH615.uByteToInt(tseg[sp]) * 256 + DeviceGH615.uByteToInt(tseg[sp + 1]);
		                    sp++;
		                    sp++;

		                    tl.MaxHR = DeviceGH615.uByteToInt(tseg[sp]);
		                    sp++;
		                    tl.AvgHR = DeviceGH615.uByteToInt(tseg[sp]);
		                    sp++;

		                    tl.Points =
		                            DeviceGH615.uByteToInt(tseg[sp + 0]) * 16777216
		                            + DeviceGH615.uByteToInt(tseg[sp + 1]) * 65536
		                            + DeviceGH615.uByteToInt(tseg[sp + 2]) * 256
		                            + DeviceGH615.uByteToInt(tseg[sp + 3]);
		                    sp++;
		                    sp++;
		                    sp++;
		                    sp++;

		                    track.tls.add(tl);

		                    if (track.Laps.equals(track.tls.size())) {
		                        sp = 2070;
		                    }
		                }
		            } else {
		                sp = 34;
		                while (sp < 2074) {
		                    TrackPoint tp = new TrackPoint();
		                    tp.Lat =
		                            DeviceGH615.uByteToInt(tseg[sp + 0]) * 16777216
		                            + DeviceGH615.uByteToInt(tseg[sp + 1]) * 65536
		                            + DeviceGH615.uByteToInt(tseg[sp + 2]) * 256
		                            + DeviceGH615.uByteToInt(tseg[sp + 3]);
		                    sp++;
		                    sp++;
		                    sp++;
		                    sp++;

		                    tp.Lon =
		                            DeviceGH615.uByteToInt(tseg[sp + 0]) * 16777216
		                            + DeviceGH615.uByteToInt(tseg[sp + 1]) * 65536
		                            + DeviceGH615.uByteToInt(tseg[sp + 2]) * 256
		                            + DeviceGH615.uByteToInt(tseg[sp + 3]);
		                    sp++;
		                    sp++;
		                    sp++;
		                    sp++;

		                    tp.Alt = DeviceGH615.uByteToInt(tseg[sp]) * 256 + DeviceGH615.uByteToInt(tseg[sp + 1]);
		                    sp++;
		                    sp++;

		                    tp.Speed = DeviceGH615.uByteToInt(tseg[sp]) * 256 + DeviceGH615.uByteToInt(tseg[sp + 1]);
		                    sp++;
		                    sp++;

		                    tp.HeartRate = DeviceGH615.uByteToInt(tseg[sp]);
		                    sp++;

		                    tp.TimeShift = DeviceGH615.uByteToInt(tseg[sp]) * 256 + DeviceGH615.uByteToInt(tseg[sp + 1]);
		                    sp++;
		                    sp++;

		                    track.tps.add(tp);

		                    if (track.Points.equals(track.tps.size())) {
		                        sp = 2075;
		                    }
		                }
		            }
		            //
		            SegCounter++;
		            command_b[0] = (byte) 0x02;
		            command_b[1] = (byte) 0x00;
		            command_b[2] = (byte) 0x01;
		            command_b[3] = (byte) 0x81;
		            command_b[4] = (byte) 0x80;

		            //Todo check
		            connection.bulkTransfer(out, command_b, command_b.length, 500);

		            connection.bulkTransfer(in, tseg, tseg.length, 5000);
		            if (tseg[0] == (byte) 0x8A) {
		                SegCounter = 501;
		            }
		        }
		        
		        return track;
		    }

}
